package com.game.framework.lock;

import com.game.framework.lock.annotation.ActionLock;
import com.game.framework.lock.annotation.WriteLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Aspect
@Component
public class LockAspect {
    // 方法元数据缓存：Method -> 参数名数组
    private static final ConcurrentHashMap<Method, String[]> PARAMETER_NAMES_CACHE = new ConcurrentHashMap<>();

    // 参数索引解析缓存：(Method, 参数表达式) -> 参数位置
    private static final ConcurrentHashMap<Method, ConcurrentHashMap<String, Integer>> PARAM_INDEX_CACHE = new ConcurrentHashMap<>();

    @Around("@annotation(com.game.framework.lock.annotation.ActionLock)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Class<ActionLock> annotationClass = ActionLock.class;
        ActionLock annotationLock = method.getAnnotation(annotationClass);
        String[] parameterIndex = annotationLock.parameterIndex();
        int hashCode = getHashCodeOfLockParameter(joinPoint, parameterIndex, method, signature);

        // 获取锁
        ReentrantLock lock = LooksManager.getActionLock(
                method.getName().hashCode(),
                getLockParameter(joinPoint, parameterIndex, method, signature)
        );
        lock.lock();
        try {
            // 执行方法
            return joinPoint.proceed();
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

    @Around("@annotation(com.game.framework.lock.annotation.WriteLock)")
    public Object handleWriteLock(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Class<WriteLock> annotationClass = WriteLock.class;
        WriteLock annotationLock = method.getAnnotation(annotationClass);
        String[] parameterIndex = annotationLock.parameterIndex();
        int hashCode = getHashCodeOfLockParameter(joinPoint, parameterIndex, method, signature);
        // 获取锁
        ReentrantLock lock = LooksManager.getWriteLock(
                annotationLock.writeclass(),
                getLockParameter(joinPoint, parameterIndex, method, signature)
        );
        lock.lock();
        try {
            // 执行方法
            return joinPoint.proceed();
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

    private int getHashCodeOfLockParameter(ProceedingJoinPoint joinPoint, String[] parameterIndex, Method method, MethodSignature signature) {
        Object[] lockParameter = getLockParameter(joinPoint, parameterIndex, method, signature);
        return Arrays.hashCode(lockParameter);
    }

    private Object[] getLockParameter(ProceedingJoinPoint joinPoint, String[] parameterIndex, Method method, MethodSignature signature) {
        Object[] args = joinPoint.getArgs();
        Object[] lockParameter = new Object[parameterIndex.length];

        // 获取参数名称（带缓存）
        String[] parameterNames = PARAMETER_NAMES_CACHE.computeIfAbsent(method, m -> signature.getParameterNames());

        // 获取参数索引映射缓存
        ConcurrentHashMap<String, Integer> indexMap = PARAM_INDEX_CACHE.computeIfAbsent(method, m -> new ConcurrentHashMap<>());

        // 填充lockParameter数组（使用缓存的索引映射）
        for (int i = 0; i < parameterIndex.length; i++) {
            String paramExpr = parameterIndex[i];
            Integer paramPosition;

            // 从缓存获取参数位置
            paramPosition = indexMap.get(paramExpr);
            if (paramPosition == null) {
                // 缓存未命中，解析并缓存
                if (paramExpr.startsWith("#")) {
                    String paramName = paramExpr.substring(1);
                    paramPosition = findParameterIndex(parameterNames, paramName);
                } else {
                    paramPosition = findParameterIndex(parameterNames, paramExpr);
                }
                if (paramPosition != null) {
                    indexMap.put(paramExpr, paramPosition);
                }
            }

            // 设置参数值
            if (paramPosition != null && paramPosition < args.length) {
                lockParameter[i] = args[paramPosition];
            }
        }
        return lockParameter;
    }


    /**
     * 查找参数名在参数列表中的索引位置
     */
    private Integer findParameterIndex(String[] parameterNames, String paramName) {
        for (int i = 0; i < parameterNames.length; i++) {
            if (parameterNames[i].equals(paramName)) {
                return i;
            }
        }
        return null;
    }

}
